<template>
  <view class="container" @touchstart="handleTouchStart" @touchend="handleTouchEnd">
    <view class="header">
      <text class="title">合成小猫咪</text>
      <view class="score-container">
        <view class="score-box">
          <text class="score-label">最高分</text>
          <text class="score-value">{{ bestScore }}</text>
        </view>
        <view class="score-box">
          <text class="score-label">得分</text>
          <text class="score-value">{{ score }}</text>
        </view>
      </view>
    </view>
    
    <view class="game-container">
      <view class="game-board">
        <view class="grid-row" v-for="(row, rowIndex) in board" :key="rowIndex">
          <view class="grid-cell" v-for="(cell, colIndex) in row" :key="colIndex">
            <view v-if="cell" class="tile-container" 
                  :class="{
                    'tile-new': isNewTile(rowIndex, colIndex),
                    'tile-merged': isMergedTile(rowIndex, colIndex)
                  }">
              <image :src="getImageForNumber(cell)" class="tile-image" mode="aspectFit"/>
            </view>
          </view>
        </view>
      </view>
    </view>

    <view class="controls">
      <button class="control-btn" @click="restart">重新开始</button>
      <button class="control-btn" @click="showLegend">查看图例</button>
      <button class="control-btn" @click="backToHome">返回主页</button>
    </view>

    <!-- 图例弹窗 -->
    <view class="legend-modal" v-if="showLegendModal" @click="showLegendModal = false">
      <view class="legend-content" @click.stop>
        <view class="legend-header">
          <text class="legend-title">猫咪图例</text>
          <text class="close-btn" @click="showLegendModal = false">×</text>
        </view>
        <view class="legend-grid">
          <view v-for="(value, index) in legendValues" :key="index" class="legend-item">
            <view class="legend-tile">
              <image :src="getImageForNumber(value)" mode="aspectFit" class="legend-cat-image"></image>
            </view>
            <text class="legend-value">{{ value }}</text>
          </view>
        </view>
      </view>
    </view>

    <!-- 游戏结束弹窗 -->
    <view class="game-over-modal" v-if="gameOver">
      <view class="game-over-content">
        <text class="game-over-title">游戏结束</text>
        <text class="game-over-score">最终得分：{{ score }}</text>
        <button class="restart-btn" @click="restart">重新开始</button>
      </view>
    </view>

    <!-- 胜利弹窗 -->
    <view class="victory-modal" v-if="victory">
      <view class="victory-content">
        <text class="victory-title">恭喜你赢了！</text>
        <text class="victory-subtitle">你已经合成了一只宇宙无敌最可爱的猫咪！</text>
        <text class="victory-score">当前得分：{{ score }}</text>
        <view class="victory-buttons">
          <button class="victory-btn continue-btn" @click="continueGame">继续游戏</button>
          <button class="victory-btn restart-btn" @click="restart">重新开始</button>
        </view>
      </view>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      board: Array(4).fill().map(() => Array(4).fill(0)),
      score: 0,
      bestScore: 0,
      touchStartX: 0,
      touchStartY: 0,
      newTiles: new Set(),
      mergedTiles: new Set(),
      gameOver: false,
      victory: false,
      showLegendModal: false,
      legendValues: [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048],
      imageCache: new Map(),
      imageMap: {
        2: '/static/images/2.png',
        4: '/static/images/4.png',
        8: '/static/images/8.png',
        16: '/static/images/16.png',
        32: '/static/images/32.png',
        64: '/static/images/64.png',
        128: '/static/images/128.png',
        256: '/static/images/256.png',
        512: '/static/images/512.png',
        1024: '/static/images/1024.png',
        2048: '/static/images/2048.png'
      },
      directionMap: {
        up: { dx: -1, dy: 0 },
        down: { dx: 1, dy: 0 },
        left: { dx: 0, dy: -1 },
        right: { dx: 0, dy: 1 }
      }
    }
  },
  computed: {
    hasEmptyCell() {
      return this.board.some(row => row.some(cell => cell === 0))
    },
    canMerge() {
      for (let i = 0; i < 4; i++) {
        for (let j = 0; j < 3; j++) {
          if (this.board[i][j] === this.board[i][j + 1]) return true
        }
      }
      for (let i = 0; i < 3; i++) {
        for (let j = 0; j < 4; j++) {
          if (this.board[i][j] === this.board[i + 1][j]) return true
        }
      }
      return false
    }
  },
  onLoad() {
    this.initGame()
    this.loadBestScore()
    this.loadImageCache()
  },
  onUnload() {
    // #ifdef H5
    document.removeEventListener('keydown', this.handleKeyPress)
    // #endif
  },
  methods: {
    initGame() {
      this.board = Array(4).fill().map(() => Array(4).fill(0))
      this.score = 0
      this.addNewTile()
      this.addNewTile()
    },
    loadBestScore() {
      try {
        const bestScore = uni.getStorageSync('bestScore')
        if (bestScore) {
          this.bestScore = parseInt(bestScore)
        }
      } catch (e) {
        console.error('读取最高分失败:', e)
      }
    },
    loadImageCache() {
      try {
        const cachedImages = uni.getStorageSync('imageCache')
        if (cachedImages) {
          const parsedCache = JSON.parse(cachedImages)
          this.imageCache = new Map(parsedCache)
          console.log('已从缓存加载图片:', this.imageCache.size)
        } else {
          // 如果没有缓存，则执行预加载
          this.preloadImages()
        }
      } catch (e) {
        console.error('加载图片缓存失败:', e)
        // 出错时执行预加载
        this.preloadImages()
      }
    },
    preloadImages() {
      const loadPromises = Object.values(this.imageMap).map(imageUrl => {
        if (!this.imageCache.has(imageUrl)) {
          return new Promise((resolve, reject) => {
            uni.getImageInfo({
              src: imageUrl,
              success: (res) => {
                this.imageCache.set(imageUrl, res.path)
                resolve()
              },
              fail: reject
            })
          })
        }
      })
      Promise.all(loadPromises).catch(err => {
        console.error('图片加载失败:', err)
      })
    },
    getImageForNumber(number) {
      const imageUrl = this.imageMap[number]
      if (this.imageCache.has(imageUrl)) {
        return this.imageCache.get(imageUrl)
      }
      return imageUrl
    },
    isNewTile(row, col) {
      return this.newTiles.has(`${row},${col}`)
    },
    isMergedTile(row, col) {
      return this.mergedTiles.has(`${row},${col}`)
    },
    addNewTile() {
      const emptyCells = []
      for (let i = 0; i < 4; i++) {
        for (let j = 0; j < 4; j++) {
          if (this.board[i][j] === 0) {
            emptyCells.push({x: i, y: j})
          }
        }
      }
      if (emptyCells.length > 0) {
        const randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)]
        
        // 根据得分调整猫咪生成概率
        let probability = {
          2: 0.9,  // 默认2的概率为90%
          4: 0.1,  // 默认4的概率为10%
          8: 0,    // 默认8的概率为0
          16: 0    // 默认16的概率为0
        }
        
        // 根据得分调整各数值猫咪的出现概率
        if (this.score >= 500) {
          probability = { 2: 0.85, 4: 0.15, 8: 0, 16: 0 }
        }
        if (this.score >= 1000) {
          probability = { 2: 0.80, 4: 0.18, 8: 0.02, 16: 0 }
        }
        if (this.score >= 2000) {
          probability = { 2: 0.75, 4: 0.20, 8: 0.05, 16: 0 }
        }
        if (this.score >= 4000) {
          probability = { 2: 0.70, 4: 0.22, 8: 0.07, 16: 0.01 }
        }
        if (this.score >= 8000) {
          probability = { 2: 0.65, 4: 0.25, 8: 0.08, 16: 0.02 }
        }
        
        // 根据概率分布生成猫咪数值
        const rand = Math.random()
        let value = 2
        let cumulative = 0
        
        for (const [val, prob] of Object.entries(probability)) {
          cumulative += prob
          if (rand < cumulative) {
            value = parseInt(val)
            break
          }
        }
        
        this.board[randomCell.x][randomCell.y] = value
        this.newTiles.clear()
        this.newTiles.add(`${randomCell.x},${randomCell.y}`)
      }
    },
    handleKeyPress(event) {
      let moved = false
      switch(event.key) {
        case 'ArrowUp':
          moved = this.moveUp()
          break
        case 'ArrowDown':
          moved = this.moveDown()
          break
        case 'ArrowLeft':
          moved = this.moveLeft()
          break
        case 'ArrowRight':
          moved = this.moveRight()
          break
      }
      if (moved) {
        this.addNewTile()
        this.checkGameStatus()
      }
    },
    moveLeft() {
      return this.move('left')
    },
    moveRight() {
      return this.move('right')
    },
    moveUp() {
      return this.move('up')
    },
    moveDown() {
      return this.move('down')
    },
    move(direction) {
      if (this.gameOver) return false
      
      const { dx, dy } = this.directionMap[direction]
      const newBoard = JSON.parse(JSON.stringify(this.board))
      let moved = false
      
      this.newTiles.clear()
      this.mergedTiles.clear()
      
      const rowOrder = dx > 0 ? [3, 2, 1, 0] : [0, 1, 2, 3]
      const colOrder = dy > 0 ? [3, 2, 1, 0] : [0, 1, 2, 3]
      
      for (let i of rowOrder) {
        const line = direction === 'left' || direction === 'right' 
          ? newBoard[i].slice()
          : newBoard.map(row => row[i])
          
        if (direction === 'right' || direction === 'down') {
          line.reverse()
        }
        
        const mergedLine = this.mergeLine(line)
        
        if (direction === 'right' || direction === 'down') {
          mergedLine.reverse()
        }
        
        if (direction === 'left' || direction === 'right') {
          newBoard[i] = mergedLine
        } else {
          for (let j = 0; j < 4; j++) {
            newBoard[j][i] = mergedLine[j]
          }
        }
        
        if (JSON.stringify(line) !== JSON.stringify(mergedLine)) {
          moved = true
        }
      }
      
      if (moved) {
        this.board = newBoard
        this.addNewTile()
        this.checkGameStatus()
      }
      
      return moved
    },
    mergeLine(line) {
      const nonZero = line.filter(x => x !== 0)
      const merged = []
      let i = 0
      
      while (i < nonZero.length) {
        if (i + 1 < nonZero.length && nonZero[i] === nonZero[i + 1]) {
          const mergedValue = nonZero[i] * 2
          merged.push(mergedValue)
          this.score += mergedValue
          this.updateBestScore()
          i += 2
        } else {
          merged.push(nonZero[i])
          i++
        }
      }
      
      return [...merged, ...Array(4 - merged.length).fill(0)]
    },
    updateBestScore() {
      if (this.score > this.bestScore) {
        this.bestScore = this.score
        try {
          uni.setStorageSync('bestScore', this.bestScore.toString())
        } catch (e) {
          console.error('保存最高分失败:', e)
        }
      }
    },
    checkGameStatus() {
      if (this.board.some(row => row.some(cell => cell === 2048))) {
        this.victory = true
      } else if (!this.hasEmptyCell && !this.canMerge) {
        this.gameOver = true
      }
    },
    handleTouchStart(event) {
      this.touchStartX = event.touches[0].clientX
      this.touchStartY = event.touches[0].clientY
    },
    handleTouchEnd(event) {
      const deltaX = event.changedTouches[0].clientX - this.touchStartX
      const deltaY = event.changedTouches[0].clientY - this.touchStartY
      const minSwipeDistance = 30
      
      if (Math.abs(deltaX) > Math.abs(deltaY)) {
        if (Math.abs(deltaX) > minSwipeDistance) {
          deltaX > 0 ? this.moveRight() : this.moveLeft()
        }
      } else {
        if (Math.abs(deltaY) > minSwipeDistance) {
          deltaY > 0 ? this.moveDown() : this.moveUp()
        }
      }
    },
    restart() {
      this.victory = false
      this.gameOver = false
      this.initGame()
    },
    backToHome() {
      uni.redirectTo({
        url: '/pages/index/index'
      })
    },
    showLegend() {
      this.showLegendModal = true
    },
    continueGame() {
      this.victory = false
    }
  }
}
</script>

<style>
page {
  height: 100%;
  overflow: hidden;
  position: fixed;
  width: 100%;
}

.container {
  padding: 0 40rpx 0 40rpx;
  background-color: #faf8ef;
  height: 100vh;
  box-sizing: border-box;
  width: 100%;
  will-change: transform;
  touch-action: none;
  display: flex;
  flex-direction: column;
  padding-top: 100rpx;
}

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 40rpx;
  width: 100%;
}

.title {
  font-size: 65rpx;
  font-weight: bold;
  color: #776e65;
}

.score-container {
  display: flex;
  gap: 20rpx;
}

.score-box {
  background-color: #bbada0;
  padding: 10rpx 20rpx;
  border-radius: 12rpx;
  text-align: center;
  min-width: 120rpx;
}

.score-label {
  color: #eee4da;
  font-size: 24rpx;
  display: block;
}

.score-value {
  color: white;
  font-size: 36rpx;
  font-weight: bold;
}

.game-container {
  background-color: #bbada0;
  padding: 20rpx;
  border-radius: 12rpx;
  margin-bottom: 40rpx;
  width: 100%;
  box-sizing: border-box;
}

.game-board {
  width: 100%;
  aspect-ratio: 1;
  display: grid;
  grid-template-rows: repeat(4, 1fr);
  gap: 10rpx;
}

.grid-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10rpx;
  width: 100%;
  height: 100%;
}

.grid-cell {
  background-color: #cdc1b4;
  border-radius: 8rpx;
  aspect-ratio: 1;
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
  width: 100%;
  height: 100%;
  transition: all 0.15s ease-in-out;
}

.tile-container {
  width: 100%;
  height: 100%;
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  transform: translateZ(0);
  will-change: transform;
}

.tile-new {
  animation: tile-appear 0.3s ease-in-out;
}

@keyframes tile-appear {
  0% {
    transform: scale(0) translateZ(0);
    opacity: 0;
  }
  50% {
    transform: scale(1.1) translateZ(0);
    opacity: 0.9;
  }
  100% {
    transform: scale(1) translateZ(0);
    opacity: 1;
  }
}

.tile-image {
  width: 90%;
  height: 90%;
  object-fit: contain;
  transition: all 0.15s ease-in-out;
}

.tile-merged {
  animation: tile-merge 0.3s ease-in-out;
}

@keyframes tile-merge {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.2);
  }
  100% {
    transform: scale(1);
  }
}

.controls {
  display: flex;
  justify-content: center;
  gap: 20rpx;
  margin-top: 40rpx;
  flex-wrap: wrap;
}

.control-btn {
  background: #8f7a66;
  color: white;
  border: none;
  padding: 20rpx 40rpx;
  border-radius: 10rpx;
  font-size: 32rpx;
  transition: all 0.3s ease;
  margin-bottom: 10rpx;
}

.control-btn:active {
  transform: scale(0.95);
  background: #7f6a56;
}

/* 图例弹窗样式 */
.legend-modal {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
  animation: fadeIn 0.3s ease;
}

.legend-content {
  background: white;
  border-radius: 30rpx;
  padding: 40rpx;
  width: 85%;
  max-width: 650rpx;
  max-height: 80vh;
  overflow-y: auto;
  animation: slideUp 0.3s ease;
}

.legend-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 30rpx;
}

.legend-title {
  font-size: 36rpx;
  font-weight: bold;
  color: #776e65;
}

.close-btn {
  font-size: 48rpx;
  color: #776e65;
  padding: 10rpx;
}

.legend-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20rpx;
}

.legend-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10rpx;
}

.legend-tile {
  width: 100rpx;
  height: 100rpx;
  border-radius: 10rpx;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #eee4da;
  box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.1);
}

.legend-cat-image {
  width: 80%;
  height: 80%;
}

.legend-value {
  font-size: 24rpx;
  color: #776e65;
  font-weight: bold;
}

/* 游戏结束弹窗样式 */
.game-over-modal {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
  animation: fadeIn 0.3s ease;
}

.game-over-content {
  background: linear-gradient(135deg, #faf8ef 0%, #f8f6ed 100%);
  border-radius: 30rpx;
  padding: 60rpx 40rpx;
  width: 80%;
  max-width: 600rpx;
  text-align: center;
  box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.2);
  animation: slideUp 0.3s ease;
  position: relative;
  overflow: hidden;
}

.game-over-content::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 8rpx;
  background: linear-gradient(90deg, #8f7a66, #f9f6f2);
}

.game-over-title {
  font-size: 48rpx;
  font-weight: bold;
  color: #776e65;
  margin-bottom: 30rpx;
  display: block;
  text-shadow: 2rpx 2rpx 4rpx rgba(0, 0, 0, 0.1);
}

.game-over-score {
  font-size: 36rpx;
  color: #776e65;
  margin-bottom: 40rpx;
  display: block;
  background: rgba(255, 255, 255, 0.8);
  padding: 20rpx 40rpx;
  border-radius: 20rpx;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}

.restart-btn {
  background: linear-gradient(135deg, #8f7a66 0%, #7f6a56 100%);
  color: white;
  border: none;
  padding: 20rpx 60rpx;
  border-radius: 30rpx;
  font-size: 32rpx;
  font-weight: bold;
  box-shadow: 0 4rpx 12rpx rgba(143, 122, 102, 0.3);
  transition: all 0.3s ease;
  position: relative;
  overflow: hidden;
}

.restart-btn::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 100%);
  opacity: 0;
  transition: opacity 0.3s ease;
}

.restart-btn:active {
  transform: translateY(2rpx);
  box-shadow: 0 2rpx 6rpx rgba(143, 122, 102, 0.2);
}

.restart-btn:active::after {
  opacity: 1;
}

/* 胜利弹窗样式 */
.victory-modal {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
  animation: fadeIn 0.3s ease;
}

.victory-content {
  background: linear-gradient(135deg, #faf8ef 0%, #f8f6ed 100%);
  border-radius: 30rpx;
  padding: 60rpx 40rpx;
  width: 80%;
  max-width: 600rpx;
  text-align: center;
  box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.2);
  animation: slideUp 0.3s ease;
  position: relative;
  overflow: hidden;
}

.victory-content::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 8rpx;
  background: linear-gradient(90deg, #8f7a66, #f9f6f2);
}

.victory-title {
  font-size: 48rpx;
  font-weight: bold;
  color: #776e65;
  margin-bottom: 20rpx;
  display: block;
  text-shadow: 2rpx 2rpx 4rpx rgba(0, 0, 0, 0.1);
}

.victory-subtitle {
  font-size: 32rpx;
  color: #776e65;
  margin-bottom: 30rpx;
  display: block;
  line-height: 1.5;
}

.victory-score {
  font-size: 36rpx;
  color: #776e65;
  margin-bottom: 40rpx;
  display: block;
  background: rgba(255, 255, 255, 0.8);
  padding: 20rpx 40rpx;
  border-radius: 20rpx;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}

.victory-buttons {
  display: flex;
  justify-content: center;
  gap: 20rpx;
}

.victory-btn {
  flex: 1;
  max-width: 200rpx;
  background: linear-gradient(135deg, #8f7a66 0%, #7f6a56 100%);
  color: white;
  border: none;
  padding: 20rpx 40rpx;
  border-radius: 30rpx;
  font-size: 32rpx;
  font-weight: bold;
  box-shadow: 0 4rpx 12rpx rgba(143, 122, 102, 0.3);
  transition: all 0.3s ease;
  position: relative;
  overflow: hidden;
}

.victory-btn::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 100%);
  opacity: 0;
  transition: opacity 0.3s ease;
}

.victory-btn:active {
  transform: translateY(2rpx);
  box-shadow: 0 2rpx 6rpx rgba(143, 122, 102, 0.2);
}

.victory-btn:active::after {
  opacity: 1;
}

.continue-btn {
  background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes slideUp {
  from {
    transform: translateY(50rpx);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}
</style> 